/*
    json2.js
    2013-05-26
    Public Domain.
	自己需要承担使用的风险
	请参考http://www.JSON.org/js.html
    This code should be minified before deployment.
          在线上环境使用，应该先进行压缩
          请参考http://javascript.crockford.com/jsmin.html
          请将该文件下载到本地服务器进行使用（使用远程未知服务器是不明智的选择）
	这个文件创建了全局变量JSON，它有两个方法stringfy和parse
        JSON.stringify(value, replacer, space)
            value       any JavaScript value, usually an object or array.
            replacer    an optional parameter that determines how object
                        values are stringified for objects. It can be a
                        function or an array of strings.
            space       an optional parameter that specifies the indentation
                        of nested structures. If it is omitted, the text will
                        be packed without extra whitespace. If it is a number,
                        it will specify the number of spaces to indent at each
                        level. If it is a string (such as '\t' or ' '),
                        it contains the characters used to indent at each level.
            This method produces a JSON text from a JavaScript value.
            When an object value is found, if the object contains a toJSON
            method, its toJSON method will be called and the result will be
            stringified. A toJSON method does not serialize: it returns the
            value represented by the name/value pair that should be serialized,
            or undefined if nothing should be serialized. The toJSON method
            will be passed the key associated with the value, and this will be
            bound to the value
            For example, this would serialize Dates as ISO strings.
                Date.prototype.toJSON = function (key) {
                    function f(n) {
                        // Format integers to have at least two digits.
                        return n < 10 ? '0' + n : n;
                    }
                    return this.getUTCFullYear()   + '-' +
                         f(this.getUTCMonth() + 1) + '-' +
                         f(this.getUTCDate())      + 'T' +
                         f(this.getUTCHours())     + ':' +
                         f(this.getUTCMinutes())   + ':' +
                         f(this.getUTCSeconds())   + 'Z';
                };
            You can provide an optional replacer method. It will be passed the
            key and value of each member, with this bound to the containing
            object. The value that is returned from your method will be
            serialized. If your method returns undefined, then the member will
            be excluded from the serialization.
            If the replacer parameter is an array of strings, then it will be
            used to select the members to be serialized. It filters the results
            such that only members with keys listed in the replacer array are
            stringified.
            Values that do not have JSON representations, such as undefined or
            functions, will not be serialized. Such values in objects will be
            dropped; in arrays they will be replaced with null. You can use
            a replacer function to replace those with JSON values.
            JSON.stringify(undefined) returns undefined.
            The optional space parameter produces a stringification of the
            value that is filled with line breaks and indentation to make it
            easier to read.
            If the space parameter is a non-empty string, then that string will
            be used for indentation. If the space parameter is a number, then
            the indentation will be that many spaces.
            Example:
            text = JSON.stringify(['e', {pluribus: 'unum'}]);
            // text is '["e",{"pluribus":"unum"}]'
            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
            text = JSON.stringify([new Date()], function (key, value) {
                return this[key] instanceof Date ?
                    'Date(' + this[key] + ')' : value;
            });
            // text is '["Date(---current time---)"]'
        JSON.parse(text, reviver)
            This method parses a JSON text to produce an object or array.
            It can throw a SyntaxError exception.
            The optional reviver parameter is a function that can filter and
            transform the results. It receives each of the keys and values,
            and its return value is used instead of the original value.
            If it returns what it received, then the structure is not modified.
            If it returns undefined then the member is deleted.
            Example:
            // Parse the text. Values that look like ISO date strings will
            // be converted to Date objects.
            myData = JSON.parse(text, function (key, value) {
                var a;
                if (typeof value === 'string') {
                    a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                    if (a) {
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
                            +a[5], +a[6]));
                    }
                }
                return value;
            });
            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
                var d;
                if (typeof value === 'string' &&
                        value.slice(0, 5) === 'Date(' &&
                        value.slice(-1) === ')') {
                    d = new Date(value.slice(5, -1));
                    if (d) {
                        return d;
                    }
                }
                return value;
            });
          这个参考实现可以任意复制、修改或者重新分发
*/
 
/*jslint evil: true, regexp: true */
 
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/
 
 
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
//如果JSON对象不存在，再创建它；否则，不需要创建。(适用于老版的IE浏览器，chrome等现代浏览器中不需要创建)
//在闭包中创建JSON对象的子方法，这样可以避免创建不要的的全局变量
 
//-----------------只是为了在chrome浏览器中测试使用，源文件中没有这部分----------------------
function test(key,value){
    console.log("this=" + this);
    console.log(key+","+value);
    return value;
}

JSON = function(){};
//-----------------------------------------------------------------------------------------

if (typeof JSON !== 'object') {
JSON = {};
}

(function () {
'use strict';
//这里可以参考http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html
//老版本的浏览器会把它当作一行普通字符串，加以忽略。

function f(n) {
    // 格式化整数（至少要达到两位数）
    return n < 10 ? '0' + n : n;
}

//如果Date的toJSON方法不存在就创建它
if (typeof Date.prototype.toJSON !== 'function') {

    Date.prototype.toJSON = function () {
        //isFinite是原生方法
        return isFinite(this.valueOf())
            ? this.getUTCFullYear()     + '-' +
                f(this.getUTCMonth() + 1) + '-' +
                f(this.getUTCDate())      + 'T' +
                f(this.getUTCHours())     + ':' +
                f(this.getUTCMinutes())   + ':' +
                f(this.getUTCSeconds())   + 'Z'
            : null;
    };

    String.prototype.toJSON      =
        Number.prototype.toJSON  =
        Boolean.prototype.toJSON = function () {
            return this.valueOf();
        };
}


var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
    //------------------------------------------------
    //控制字符：[\x00-\x1F\x7F] 其他的还未知 
    escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
    //------------------------------------------------
    gap,
    indent,
    meta = {    // 字符替换表格
        '\b': '\\b',
        '\t': '\\t',
        '\n': '\\n',
        '\f': '\\f',
        '\r': '\\r',
        '"' : '\\"',
        '\\': '\\\\'
    },
    rep;


function quote(string) {

    //如果字符串没有控制字符、引用字符、反斜杠字符，我们可以直接在字符左右两边添加引号；
    //否则，必须先进行替换
    escapable.lastIndex = 0;
    return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
        var c = meta[a];
        return typeof c === 'string'
            ? c
            : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
    }) + '"' : '"' + string + '"';
}


function str(key, holder) {

// 取得holder[key]对应的string

    var i,          // 循环计数器
        k,          // 成员变量key.
        v,          // 成员变量值.
        length,
        mind = gap,
        partial,
        value = holder[key];

//如果value有toJSON方法，就调用它获取替换后的值
    if (value && typeof value === 'object' &&
            typeof value.toJSON === 'function') {
        value = value.toJSON(key);
    }

//如果rep是一个function，就调用它获取替换后的值
    if (typeof rep === 'function') {
        value = rep.call(holder, key, value);
    }

//根据value的类型进行判断
    switch (typeof value) {
    case 'string':
        return quote(value);

    case 'number':

//数字类型必须是有限的，非有限的数字设置成null
        return isFinite(value) ? String(value) : 'null';

    case 'boolean':
    case 'null':

//如果是boolean类型或者null，将它转化成字符串
//需要注意的是typeof null并不等于'null'，这里这么写是为了将来修复以后使用。
        return String(value);

//如果是object类型，必须进一步区分出object、array、null类型
    case 'object':

//由于script规范的错误，typeof null == 'object',所以这里区分出了这种情况

        if (!value) {
            return 'null';
        }

//初始化partial数组来存放局部结果
        gap += indent;
        partial = [];

//如果value是数组
        if (Object.prototype.toString.apply(value) === '[object Array]') {

//遍历value中的每一个元素进行stringify，存放到partial数组中，如果不存在，存入"null"字符串到partial数组中。

            length = value.length;
            for (i = 0; i < length; i += 1) {
                partial[i] = str(i, value) || 'null';
            }

//将partial数组中的元素以逗号区分、放到括号内并返回
            v = partial.length === 0
                ? '[]'
                : gap
                ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
                : '[' + partial.join(',') + ']';
            gap = mind;
            return v;
        }

//如果replacer参数是数组，use it to select the members to be stringified.
        if (rep && typeof rep === 'object') {
            length = rep.length;
            for (i = 0; i < length; i += 1) {
                if (typeof rep[i] === 'string') {
                    k = rep[i];
                    v = str(k, value);
                    if (v) {
                        partial.push(quote(k) + (gap ? ': ' : ':') + v);
                    }
                }
            }
        } else {

//否则，遍历value中的所有属性
            for (k in value) {
                if (Object.prototype.hasOwnProperty.call(value, k)) {
                    v = str(k, value);
                    if (v) {
                        partial.push(quote(k) + (gap ? ': ' : ':') + v);
                    }
                }
            }
        }

//将partial数组中的元素以逗号区分、放到括号内并返回

        v = partial.length === 0
            ? '{}'
            : gap
            ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
            : '{' + partial.join(',') + '}';
        gap = mind;
        return v;
    }
}

//如果JSON对象没有stringfy方法，就定义它
if (typeof JSON.stringify !== 'function') {
    JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

        var i;
        gap = '';
        indent = '';

//如果space是数字类型，就创建相应大小的空格作为缩进

        if (typeof space === 'number') {
            for (i = 0; i < space; i += 1) {
                indent += ' ';
            }

//如果space是string类型，就将space赋值给indent作为缩进
        } else if (typeof space === 'string') {
            indent = space;
        }

//如果replacer存在，必须是function获取array；否则将抛出异常

        rep = replacer;
        if (replacer && typeof replacer !== 'function' &&
                (typeof replacer !== 'object' ||
                typeof replacer.length !== 'number')) {
            throw new Error('JSON.stringify');
        }

//将value放到key为''的父包装对象中
        return str('', {'': value});
    };
}


//如果JSON对象没有parse方法，就定义它
if (typeof JSON.parse !== 'function') {
    JSON.parse = function (text, reviver) {

//这个方法有两个参数：text和reviver（可选）
//如果json格式正确，返回对应的js对象
        var j;

        function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
//walk方法用来递归调用

            var k, v, value = holder[key];
            if (value && typeof value === 'object') {
                for (k in value) {
                    if (Object.prototype.hasOwnProperty.call(value, k)) {
                        v = walk(value, k);
                        if (v !== undefined) {
                            value[k] = v;
                        } else {
                            delete value[k];
                        }
                    }
                }
            }
            return reviver.call(holder, key, value);
        }


//paring过程分为4个步骤。
//第一个步骤是将unicode字符替换为转义字符。
//js在处理多种字符时是有问题的，不是悄悄的删掉他们，就是把他们当作行结束符。
        text = String(text);
        cx.lastIndex = 0;
        if (cx.test(text)) {
            text = text.replace(cx, function (a) {
                return '\\u' +
                    ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            });
        }
        
//第二个步骤如下：
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

        if (/^[\],:{}\s]*$/
                .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                    .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                    .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

//第三步骤：调用eval命令
//'{'在js中有语法歧义倾向：可以是程序块或者对象字面值。所以这里使用括号来避免歧义            	
            j = eval('(' + text + ')');

//第四步骤（可选）：如果reviver是一个function，则递归调用walk方法（参数是name/value对）
            return typeof reviver === 'function'
                ? walk({'': j}, '')
                : j;
        }

// 如果text不可转化，抛出异常
        throw new SyntaxError('JSON.parse');
    };
}
}());